#include "fxcc/core/graph/opengl3/ImGuiGlfwApp.h"
#include "fxcc/core/graph/opengl3/Resources.h"
#include "fxcc/core/graph/opengl3/Mesh2.h"
#include "fxcc/core/graph/common/Geometry.h"
#include "fxcc/imgui/Editor.h"

using namespace fxcc::platform;
using namespace fxcc::graph;
using namespace fxcc::graph::opengl3;
using namespace fxcc::imgui::graph::opengl3;
using BaseApp = fxcc::imgui::graph::opengl3::GlfwApp;

#undef main
struct ToonEffect : public fxcc::graph::opengl3::Effect3d
{
    ToonEffect() : fxcc::graph::opengl3::Effect3d(FragCode()) {

    }
    void SetLight(const glm::vec3 &pos, const glm::vec3 &color)
    {
        m_Shader->use();
        m_Shader->setVec3("lightPos", pos);
        m_Shader->setVec3("lightColor", color);
    }
    void InstMesh(const fxcc::graph::opengl3::Mesh* mesh,const fxcc::graph::common::Transform& transform, const glm::vec3 color)
    {
        m_Shader->use();
        m_Shader->setVec3("objectColor", color);
        fxcc::graph::opengl3::Effect3d::InstMesh(mesh, transform);
    }
    static std::string FragCode()
    {
        return R"(
out vec4 FragColor;

uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 objectColor;

void main(){
    vec3 norm=normalize(Normal);
    vec3 lightDir=normalize(lightPos-FragPos);

    float diff=max(dot(norm, lightDir),0.0f);

    float levels =3.0f;
    diff=floor(diff* levels)/levels;

    vec3 result=diff*lightColor* objectColor;
    FragColor=vec4(result, 1.0f);
}

        )";
    }
};
struct SimpleApp : public BaseApp
{
public:
    fxcc::graph::common::Transform transform;

    std::shared_ptr<fxcc::graph::opengl3::RenderTarget> m_RenderTarget;
    std::shared_ptr<fxcc::graph::opengl3::Mesh> m_CubeMesh;
    std::shared_ptr<fxcc::graph::opengl3::Resources> m_Resources;
    glm::vec4 color;
    std::shared_ptr<ToonEffect> m_ToonEffect;

    fxcc::graph::common::Camera camera;
    glm::vec3 objectColor, lightColor, lightPos;
    SimpleApp(const fxcc::platform::common::App::Desc &desc) : BaseApp(desc), color(1.0)
    {
    }
    bool Init() override
    {
        if (!BaseApp::Init())
        {
            return false;
        }

        if (!InitResources())
        {
            return false;
        }

        return true;
    };

    void printOpenGLESVersion()
    {
        const char *version = (const char *)glGetString(GL_VERSION);
        std::cout << "OpenGL ES Version: " << version << std::endl;
    };

    bool InitResources()
    {
        auto &am = fxcc::AssertManager::Inst();

        lightColor = glm::vec3(0.5f, 1.0f, 0.5f);
        lightPos = glm::vec3(0.f, 2.0f, 0.0f);
        objectColor = glm::vec3(1.f, 1.0f, 1.0f);

        camera.SetPosition(glm::vec3(5.0f));
        camera.LookAt(glm::vec3(0.0f));

        m_ToonEffect = std::make_shared<ToonEffect>();
        auto geoData = fxcc::graph::common::Geometry::CreateBox();
        fxcc::graph::common::MeshDesc meshDesc(geoData);
        m_CubeMesh = std::make_shared<fxcc::graph::opengl3::Mesh>(meshDesc);

        m_Resources = std::make_shared<fxcc::graph::opengl3::Resources>();
        if (!m_Resources->LoadScene(am.GetFile("anim/Walking.fbx")))
        {
            ztclog::info("failed load anim fbx");
            return false;
        }

        transform.m_Scale = glm::vec3(0.02f);
        return true;
    };

    fxcc::imgui::Editor editor;

    void OnEdit() override
    {
        ImGui::Begin("Hello world");
        editor.ColorInst<glm::vec3>("lightColor", lightColor);
        editor.EditInst<glm::vec3>("lightPos", lightPos);
        editor.ColorInst<glm::vec3>("objectColor", objectColor);

        ImGui::End();
    }
    void OnDestroy() override
    {

        BaseApp::OnDestroy();
    }
    void OnFrameRender() override
    {
        glViewport(0, 0, m_Desc.m_Size.x, m_Desc.m_Size.y);

        glClearColor(color.x, color.y, color.z, color.w);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // m_RenderTarget->Begin();

        m_ToonEffect->Begin();
        m_ToonEffect->SetCamera(camera);
        // m_Effect->InstMesh(m_CubeMesh.get(), transform);
        m_ToonEffect->SetLight(lightPos, lightColor);
        for (auto it : m_Resources->m_MeshDatas)
        {
            auto dataIndex = it.first;
            auto fixedMesh = m_Resources->m_FixedMeshes[dataIndex];

            m_ToonEffect->InstMesh(fixedMesh.get(), transform, objectColor);
        }
        // for (const auto &obj : m_objects)
        // {
        //     auto tex = obj.texture;
        //     tex->Active(1);

        //     m_Effect->InstMesh(obj.mesh.get(), obj.transform.GetWorld());
        // }
        m_ToonEffect->End();

        // m_ImageEffect->Begin();
        // m_ImageEffect->End();

        // m_RenderTarget->End();
    };

    void OnFramebuffersize(int w, int h) override
    {
        opengl3::GlfwApp::OnFramebuffersize(w, h);
        camera.SetAspect(m_Desc.Aspect());

        //{
        //	fxcc::graph::common::RenderTargetDesc desc;
        //	desc.InitDesc2(1, w, h);
        //	m_RenderTarget = std::make_shared<fxcc::graph::opengl3::RenderTarget>(desc);
        //}
    }
    void OnUpdate() override
    {
        opengl3::GlfwApp::OnUpdate();

        float ctTime = m_GlfwlTimer.m_CurrentTime;
        camera.FreeMove(m_Input, m_GlfwlTimer.m_DeltaTime);

        float deltaTime = m_GlfwlTimer.m_DeltaTime;
    }
};

int main()
{
    ztclog::pushFunction([](const ztclog::Line &line)
                         {
		if (line.m_Type != ztclog::Line::Type::_line_debug_)
		{
			std::cout << line.m_Text << std::endl;
		} });

    using _App = SimpleApp;
    using _Desc = fxcc::platform::common::App::Desc;

    _Desc desc;
    desc.m_Pos = glm::ivec2(100, 100);
    desc.m_Size = glm::ivec2(900, 600);
    desc.m_ScaleFactor = 1.0f;

    desc.m_Title = "test toon";
    std::shared_ptr<_App> _app = std::make_shared<_App>(desc);
    return _app->Run();
};
